<?php
if(!defined('NORMAL_RUN')) exit();

/*
 * Абстрактный класс:
 * предоставление доступа к базе данных
 *
 * Выполняет подключение к базе данных и централизованно предоставляет доступ
 * к объекту интерфейса бд
 *
 * - Инициализация: DB::connect();
 * - Состояние: DB::state();
 *
 * - Использование: DB::$mysqli->query($sql); (стандартный PHP объект Mysqli)
 */
abstract class MSDB {
                             //test-mssql.coj0ztap8tcy.eu-west-1.rds.amazonaws.com
    private static $DB_HOST = "mssql";    // сервер БД
    public  static $DB_NAME = "reports";        // имя БД
    private static $DB_LOGIN = "reports";       // имя пользователя БД
    private static $DB_PASSWORD = "reports42";  // пароль БД

    private static $_connected = false;         // флаг соединения
    private static $_state = true;              // флаг состояния класса
    private static $_error = '';                // текущее сообщение об ошибке
    private static $_isWin = null;              // Для разных систем разные способы общения
    private static $dbc;                        // интерфейс подключения к БД
    
    public static $lastReqState = true;         // состояние последнего запроса
    public static $lastRequest = '';         // состояние последнего запроса
    /**
     * Проверка системы "на Винду" и установка свойства класса
     * 
     * @return boolean true=Windows, false=other
     */
    private static function checkSystem() {
        $path = Arr::getValue($_SERVER, 'PATH', false);
        $win = ($path && strpos(strtoupper($path),  'WINDOWS'));
        self::$_isWin = $win;
        return self::$_isWin;
    } 
    
    /**
     * Проверка необходимости подключения к БД.
     * Возвращает true, если не было попыток подключиться
     * 
     * @return boolean true - можно и нужно подключаться; false - уже подключено либо подключение невозможно
     * 
     * 
     */
    private static function needConnect() {
        $out = !self::$_connected && self::$_state;
        return $out;
    }
    
    /**
     * Конвертация всего в UTF8
     * @param string $string Строка, которую конвертировать
     * @return string
     */
    private static function toUTF($string) {
        return iconv('CP866','UTF-8', $string);
    }
    
    /**
     * Возвращает статус соединения
     * @return boolean true если соединение установлено.
     */
    public static function state() {
        $out = self::$_connected && self::$_state;
        return $out;
    }


    /**
     * Соединение с базой данных
     * В случае ошибки повторная автоматическая попытка соединения не производится.
     * @return boolean true если соединение установлено.
     */
    public static function connect() {
        if (!self::needConnect()) return self::state();
        
        if (class_exists('Trace')) Trace::rec('MSDB:: connect');
        if (self::checkSystem()) {
            // Windows
            self::$dbc = sqlsrv_connect( 
                    self::$DB_HOST, //'REPORTS-2\REPORTSLOCAL',//
                    array( 
                        "Database"  => self::$DB_NAME, 
                        "UID"       => self::$DB_LOGIN, 
                        "PWD"       => self::$DB_PASSWORD
                    )
            );
        }
        else {
            // *NIX
            self::$dbc = mssql_connect(self::$DB_HOST, self::$DB_LOGIN, self::$DB_PASSWORD);
        }
        
        if (!self::$dbc) {
            self::setError('MSSQL connect error');
            self::$_state = false;
            self::$lastReqState = false;
            return false;
        }
        
        self::$_connected = true;
        if (class_exists('Trace')) Trace::rec('MSDB:: connected');
        
        if (!self::$_isWin) mssql_select_db(self::$DB_NAME);
        self::reset();
        return true;
    }


    /**
     * Закрытие соединения с БД
     */
    public static function close() {
        if (!self::$_connected) return ;
        if (self::$_isWin) sqlsrv_close(self::$dbc);
        else mssql_close(self::$dbc);
        self::$_connected = false;
        self::reset();
    }


    /**
     * Сброс состояния ошибки
     */
    public static function reset() {
        self::$_error = '';
        self::$_state = true;
        self::$lastReqState = true;
        self::$lastRequest = '';
    }


    /**
     * Установка сообщения об ошибке
     */
    private static function setError($error=''){
        if (class_exists('Trace')) Trace::rec('MSDB ERROR::  '.$error);
        self::$lastReqState = false;
        self::$_error = $error;
	}


    /**
     * Получение сообщения об ошибке в функциях класса
     * @return string Текущее сообщение об ошибке
     */
    public static function getError(){
        return self::$_error;
    }


    
    public static function query($query) {
        if (!self::connect()) return false;
        self::$lastReqState = true;
        self::$lastRequest = $query;
        if (class_exists('Trace')) Trace::rec('MSDB CALL query::  '.$query);
        
        if(self::$_isWin){
            $res = sqlsrv_query(self::$dbc, $query);
            if( $res === false ) {
                $err = 'MSDB ERROR: ';
                if( ($errors = sqlsrv_errors() ) != null) {
                    foreach( $errors as $error ) $err .= "  ".self::toUTF (Arr::getValue ($error, 'message', 'Unknown'))." <br>";
                }
                self::setError($err);
                return false;
            }
        }
        else{
            $err = false;
            $res = @mssql_query($query, self::$dbc);
            if (!$res) {
                $err = @mssql_get_last_message();
                if (!$err) $err = 'Неизвестная ошибка';
                self::setError($err);
                return false;
            }
            
        }
        
        
        if (class_exists('Trace')) Trace::rec('MSDB CALL query::  success; aff_rows='.self::affectedRows($res) );
        return $res;
    }
    
    
    
    /**
     * Affected Rows (ХЗ, насколько это работоспособно. Лучше не пользоваться)
     * 
     * @param  $res Актуален только для sqlsrv (Win)
     * @return int/false
     */
    public static function affectedRows($res=false) {
        if (!self::connect() || !($res && self::$_isWin)) return false;
        if(self::$_isWin) return sqlsrv_rows_affected($res);
        else return mssql_rows_affected(self::$dbc);
    }
    
    /**
     * Ид последней вставленной строки. 
     * Применять только для запросов INSERT!!!!
     * 
     * @param DB_resource $res
     * @return int/false
     */
    public static function insertedId() {
        if (!self::connect()) return false;
        if(self::$_isWin) {
            $res = sqlsrv_query(self::$dbc, "SELECT @@identity AS ID");
            $r = sqlsrv_fetch_array($res, SQLSRV_FETCH_ASSOC);
            return Arr::getValue($r, 'ID', false);
        }
        else {
            $res = mssql_query("SELECT @@identity AS ID");
            $r = mssql_fetch_assoc(self::$dbc);
            return Arr::getValue($r, 'ID', false); 
        }
    }
    
    /* не работает в Виндах =(
    public static function numRows($res) {
        if (!self::connect() || !$res) return false;
        if(self::$_isWin) return sqlsrv_num_rows($res);
        else return mssql_num_rows(self::$dbc);
    }
     */
    
    /**
     * Разбор очередной строки результата запроса в виде ассоциативного массива
     * 
     * @param DB_resource $res результат запроса
     * @return array/boolean Ассоциативный массив с данными строки либо false
     */
    public static function fetchAssoc($res) {
        if (!self::connect() || !$res) return false;
        if(self::$_isWin) return sqlsrv_fetch_array($res, SQLSRV_FETCH_ASSOC);
        else return mssql_fetch_assoc($res);
    }
    
    public static function freeResult($res) {
        if (!self::connect() || !$res) return false;
        if(self::$_isWin) return  sqlsrv_free_stmt($res);
        else return mssql_free_result($res);
    }
    
    
    public static function transactionBegin() {
        if (!self::connect()) return false;
        if(self::$_isWin) return sqlsrv_begin_transaction(self::$dbc);
        else return self::query('BEGIN TRANSACTION');
    }
    
    public static function transactionCommit() {
        if (!self::connect()) return false;
        if(self::$_isWin) return sqlsrv_commit(self::$dbc);
        else return self::query('COMMIT');
    }
    
    public static function transactionRollback() {
        if (!self::connect()) return false;
        if(self::$_isWin) return sqlsrv_rollback(self::$dbc);
        else return self::query('ROLLBACK');
    }
    

}


?>